package com.xframework.security.manage;

import java.util.*;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.xframework.Base;
import com.xframework.redis.XRedisTemplate;
import com.xframework.manage.BaseManage;
import com.xframework.security.dao.SecurityDao;
import com.xframework.security.model.*;
import com.xframework.security.util.Constant;
import com.xframework.security.util.RSAUtil;

@Service
public class SecurityManage extends BaseManage {

    @Autowired
    private SecurityDao securityDao;

    public SecurityManage() {
    }


    /**
     * 查询账户、账户扩展属性、账户下的资源
     *
     * @param account       Account
     * @param applicationId String
     * @return Account
     * @throws Exception
     */
    public SecurityAccount selectAccount(Account account, String applicationId, String accountIp) throws Exception {
        SecurityAccount securityAccount = null;
        String accountId = account.getAccountId();
        String accountName = account.getAccountName();
        String decryptedPassword = account.getAccountPassword();
        if (Base.isString(accountId) || Base.isString(accountName)) {
            account.setAccountPassword(null);
            List<Account> list = securityDao.selectAccountList(account);
            if (list.size() > 0) {
                account = list.get(0);
                if (this.isNotNull(decryptedPassword)) {
                    String encryptedPassword = account.getAccountPassword();
                    if (!RSAUtil.verify(decryptedPassword, encryptedPassword)) {
                        return securityAccount;
                    }
                }
                securityAccount = this.insertAccountLog(account, applicationId, accountIp);
            }
        }
        return securityAccount;
    }

    public SecurityAccount insertAccountLog(Account account, String applicationId, String accountIp) throws Exception {
        String accountId = account.getAccountId();
        String accountName = account.getAccountName();

        // 获取账户角色
        AccountRole accountRole = new AccountRole();
        accountRole.setAccountId(accountId);
        List<Role> roleList = securityDao.selectAccountRoleList(accountRole);

        // 获取账户资源
        SecurityAccount securityAccount = new SecurityAccount();
        securityAccount.setApplicationId(applicationId);
        securityAccount.setAccountId(accountId);

        String key = Constant.SECURITY_APPLICATION + Constant.UNDERLINE + applicationId;
        Map<String, String> applicationResourceUrlMap = XRedisTemplate.get(key, Map.class);
        if (applicationResourceUrlMap == null) {
            Resource applicationResource = new Resource();
            applicationResource.setApplicationId(applicationId);
            applicationResource.setResourceState(1);
            List<Resource> applicationResourceList = securityDao.selectResourceList(applicationResource);
            for (Resource resource : applicationResourceList) {
                String resourceUrl = resource.getResourceUrl();
                if (Base.isString(resourceUrl)) {
                    if (applicationResourceUrlMap == null) {
                        applicationResourceUrlMap = new HashMap<String, String>();
                    }
                    applicationResourceUrlMap.put(resourceUrl, Base.BLANK);
                }
            }
            if (applicationResourceUrlMap != null) {
                XRedisTemplate.setObject(key, applicationResourceUrlMap);
            }
//        } else {
//            applicationResourceUrlMap = XRedisTemplate.get(key, Map.class);
        }

        List<Resource> accountResourceList = securityDao.selectResourceByAccountApplication(securityAccount);
        if (accountResourceList.size() > 0) {
            StringBuilder sb = new StringBuilder();
            accountResourceList.forEach(resource -> {
                String resourceUrl = resource.getResourceUrl();
                if (Base.isString(resourceUrl)) {
                    sb.append(resource.getResourceUrl());
                    sb.append(Base.COMMA);
                }
            });
            securityAccount.setResourceUrls(sb.toString());
        }
        accountResourceList = this.treeResourceList(accountResourceList);
        securityAccount.setAccountName(accountName);
        securityAccount.setAccountRemark(account.getAccountRemark());
        securityAccount.setRoleList(roleList);
        securityAccount.setResourceList(accountResourceList);
        Application application = new Application();
        application.setApplicationId(applicationId);
        List<Application> applicationList = securityDao.selectApplicationList(application);
        if (applicationList.size() > 0) {
            application = applicationList.get(0);
            securityAccount.setApplication(application);
        }
        // 获取账户的扩展属性信息
        Property property = new Property();
        property.setAccountId(accountId);
        List<Property> propertyList = securityDao.selectAccountPropertyList(property);
        // 把账户下所有资源的一维List转化为Map
        Map<String, String> map = new HashMap<String, String>();
        propertyList.forEach(pro -> {
            map.put(pro.getPropertyKey(), pro.getPropertyValue());
        });
        securityAccount.setAccountPropertyMap(map);

        XRedisTemplate.setObject(Constant.SECURITY_ACCOUNT + Constant.UNDERLINE + applicationId + Constant.UNDERLINE + accountId, securityAccount, Constant.securityJwtExpiration);

        //修改登录时间
        String date = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
        account = new Account();
        account.setAccountId(accountId);
        account.setAccountLoginTime(date);
        this.update(account);

        // 插入账户登录日志
        AccountLog accountLog = new AccountLog();
        accountLog.setAccountId(accountId);
        accountLog.setAccountName(accountName);
        accountLog.setAccountIp(accountIp);
        accountLog.setAccountLoginTime(date);
        this.insert(accountLog);
        return securityAccount;
    }

    /**
     * 把账户下所有资源的一维List转化为二维List
     *
     * @param resourceList List
     * @return List
     * @throws Exception
     */
    public List<Resource> treeResourceList(List<Resource> resourceList) throws Exception {
        List<Resource> menuList = new ArrayList<Resource>();
        resourceList.forEach(menu -> {
            if (menu.getResourceParentId().equals(Constant.MENU_INIT_ID)) {
                menuList.add(menu);
            }
        });
        menuList.forEach(menu -> {
            menu = this.getResourceList(menu, resourceList);
        });
        return menuList;
    }

    /**
     * 递归查询资源生成树形资源List
     *
     * @param menu      Resource
     * @param menuItems List
     * @return Resource
     */
    private Resource getResourceList(Resource menu, List<Resource> menuItems) {
        List<Resource> resourceList = new ArrayList<Resource>();
        for (Resource menuItem : menuItems) {
            if (menu.getResourceId().equals(menuItem.getResourceParentId())) {
                menuItem = this.getResourceList(menuItem, menuItems);
                resourceList.add(menuItem);
            }
        }
        if (resourceList.size() > 0) {
            menu.setResourceList(resourceList);
        }
        return menu;
    }

    public Account insertAccount(Account account) throws Exception {
        String date = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
        account.setAccountCreateTime(date);
        account.setAccountModifyTime(date);
        account = this.insert(account);
        String accountId = account.getAccountId();
        Property property = new Property();
        property.setAccountId(accountId);
        property.setPropertyKey(Constant.ACCOUNT_NAME);
        property.setPropertyValue(account.getAccountName());
        property.setPropertyCreateTime(date);
        property.setPropertyModifyTime(date);
        this.insert(property);
        Map<String, String> propertyMap = account.getAccountPropertyMap();
        if (propertyMap != null) {
            for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
                property.setPropertyKey(entry.getKey());
                property.setPropertyValue(entry.getValue());
                this.insert(property);
            }
        }
        AccountRole accountRole = new AccountRole();
        accountRole.setAccountId(accountId);
        this.insertAccountRole(accountRole, account.getRoleList());
        return account;
    }

    public int updateAccount(Account account) throws Exception {
        int flag = 0;
        String accountId = account.getAccountId();
        if (this.isString(accountId)) {
            String date = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
            String password = account.getAccountPassword();
            if (password != null) {
                account.setAccountPassword(RSAUtil.encryptByPublicKey(password));
            }
            account.setAccountModifyTime(date);
            this.update(account);

            Map<String, String> propertyMap = account.getAccountPropertyMap();
            if (propertyMap != null) {
                Property property = new Property();
                property.setAccountId(accountId);
                property.setPropertyModifyTime(date);
                property.setPropertyKey(Constant.ACCOUNT_NAME);
                property.setPropertyValue(account.getAccountName());
                this.update(property);
                for (Map.Entry<String, String> entry : propertyMap.entrySet()) {
                    property.setPropertyKey(entry.getKey());
                    property.setPropertyValue(entry.getValue());
                    this.update(property);
                }
            }
            AccountRole accountRole = new AccountRole();
            accountRole.setAccountId(accountId);
            this.insertAccountRole(accountRole, account.getRoleList());
            flag = 1;
        }
        return flag;
    }

    public int deleteAccount(Account account) throws Exception {
        int flag = 0;
        String accountId = account.getAccountId();
        if (this.isString(accountId)) {
            AccountRole accountRole = new AccountRole();
            accountRole.setAccountId(accountId);
            this.delete(accountRole);
            this.delete(account);
            flag = 1;
        }
        return flag;
    }

    public void insertAccountRole(AccountRole accountRole, List<Role> roleList) throws Exception {
        if (this.isNotNull(roleList)) {
            String date = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
            this.delete(accountRole);
            accountRole.setAccountRoleCreateTime(date);
            for (Role role : roleList) {
                accountRole.setRoleId(role.getRoleId());
                this.insert(accountRole);
            }
        }
    }

    public Role insertRole(Role role) throws Exception {
        String date = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
        role.setRoleCreateTime(date);
        role.setRoleModifyTime(date);
        role = this.insert(role);
        RoleResource roleResource = new RoleResource();
        roleResource.setRoleId(role.getRoleId());
        int index = 0;
        List<Resource> resourceList = role.getResourceList();
        for (Resource resource : resourceList) {
            roleResource.setResourceId(resource.getResourceId());
            roleResource.setResourceSeq(index++);
            roleResource.setRoleResourceCreateTime(date);
            this.insert(roleResource);
        }
        return role;
    }

    public ThirdRole insertThirdRole(ThirdRole thirdRole) throws Exception {
        Role role = thirdRole.getRole();
        role = this.insertRole(role);
        thirdRole.setRoleId(role.getRoleId());
        this.insert(thirdRole);
        return thirdRole;
    }

    public void updateRole(Role role) throws Exception {
        RoleResource roleResource = new RoleResource();
        roleResource.setRoleId(role.getRoleId());
        this.delete(roleResource);
        int index = 0;
        String date = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
        List<Resource> resourceList = role.getResourceList();
        for (Resource resource : resourceList) {
            roleResource.setResourceId(resource.getResourceId());
            roleResource.setResourceSeq(index++);
            roleResource.setRoleResourceCreateTime(date);
            this.insert(roleResource);
        }
        role.setRoleModifyTime(date);
        this.update(role);
    }

    public void deleteRole(Role role) throws Exception {
        String roleId = role.getRoleId();
        if (roleId != null) {
            RoleResource roleResource = new RoleResource();
            roleResource.setRoleId(roleId);
            this.delete(roleResource);
            this.delete(role);
        }
    }

    public void insertRoleResource(RoleResource roleResource, String resourceIds) throws Exception {
        this.delete(roleResource);
        String roleResourceCreateTime = getCurrentDate(0, Base.YYYY_MM_DD_HH_MM_SS);
        if (this.isString(resourceIds)) {
            String[] array = resourceIds.split(Base.COMMA);
            int i = 1;
            for (String resourceId : array) {
                roleResource.setResourceId(resourceId);
                roleResource.setRoleResourceCreateTime(roleResourceCreateTime);
                roleResource.setResourceSeq(i++);
                this.insert(roleResource);
            }
        }
    }
}
